using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using System.Diagnostics;
using System.Xml.Serialization;

namespace Dragoon.BreakIt
{
    namespace Data
    {
        public class Level
        {
            [XmlArrayItem(typeof(string))]
            [XmlArrayItem("Row")]
            public string[] Rows;

        }
    }

    
    
    public class Capsule : Movable, ICollisionSource
    {
        public readonly CapsuleType Type;
        private int _animationHeight;
        internal Capsule(CapsuleType type, Vector3 dimensions, Vector3 origen )
            : base(dimensions ,origen,type.Speed)
        {
            Type = type;
            _animationHeight = (int) (dimensions.Y / 7);
        }

        public Rectangle ImageSourceArea
        {
            get
            {
                Rectangle result = Type.ImageSourceArea;
                int index = (int) (Position.Y / _animationHeight) % 7;
                result.Y += index * 32;
                return result;
            }
        }

        internal void StartFall(GameTime time)
        {
            Show();
            StartMoving(time, Geometry.Down);
        }

        internal void Stop(GameTime time)
        {
            Hide();
        }


        internal void Initialise()
        {
            if (IsVisible)
                Hide();
        }
    }

    public class Block : ICollidable
    {
        public readonly BlockType Type;
        public readonly Rectangle Position;
        private readonly BoundingBox _box;
        private int _hitsRemaining = 0;
        public readonly Capsule HiddenCapsule; 
        internal Block(BlockType type,CapsuleType hiddenType, Rectangle pos)
        {
            Type = type;
            Position = pos;
            if (hiddenType != null)
            {
                HiddenCapsule = new Capsule(hiddenType,
                    new Vector3(pos.Width, pos.Height, 1.0F),
                    Geometry.ConvertCenterToVector(pos));
            }
            else
                HiddenCapsule = null;
            _box = new BoundingBox(
                        Geometry.ConvertToVector(new Point(pos.Left,pos.Top)),
                        Geometry.ConvertToVector(new Point(pos.Right,pos.Bottom))
                        );
        }

        internal void Initialise()
        {
            _hitsRemaining = Type.MaxHitCount;
            if (HasCapsule)
                HiddenCapsule.Initialise();
        }

        public bool IsActive
        {
            get { return _hitsRemaining > 0; }
        }

        /// <summary>
        /// Called when the block is hit
        /// </summary>
        internal void Hit()
        {
            _hitsRemaining--;
        }

        public BoundingBox Bounds
        {
            get { return _box; }
        }

        public bool HasCapsule
        {
            get { return HiddenCapsule != null; }
        }

    }
    
    public class Level : DataBased<Data.Level>
    {

        /// <summary>
        /// Number of blocks on Y Axis
        /// </summary>
        public readonly int CountY;

        /// <summary>
        /// Number of blocks on X Axis
        /// </summary>
        public readonly int CountX;


        private readonly List<Block> _blocks;

        public Level(Data.Level data, BlockTypeSet types, CapsuleTypeSet capsules, Rectangle innerPlayArea, 
            Point blockSize)
            : base(data)
        {
            Debug.Assert(data != null);
            Debug.Assert(data.Rows != null);
            CountY = data.Rows.Length;
            if (CountY > 0)
                CountX = data.Rows[0].Length / 2;
            else
                CountX = 0;

            // Add the block at the correct place
            _blocks = new List<Block>();
            Point offset = new Point(
                innerPlayArea.X + (innerPlayArea.Width - blockSize.X * CountX) / 2,
                innerPlayArea.Y + (innerPlayArea.Height - blockSize.Y * CountY) / 2);
            if (offset.X < 0)
                throw new ExDragoon("X offset {0}", offset.X);
            if (offset.Y < 0)
                throw new ExDragoon("XYoffset {0}", offset.Y);
            Point walkingOffset = new Point(offset.X,offset.Y);
            foreach (string row in data.Rows)
            {
                if (row.Length != CountX * 2)
                    throw new ExDragoon("Invalid row '{0}', it has {1} digits.  Expected {2} digits", row, row.Length, CountX*2);
                int i = 0;
                while (i < row.Length)
                {
                    char blockKey = row[i++];
                    char capsuleKey = row[i++];
                    if (blockKey != '.')
                    {
                        _blocks.Add(
                            new Block(
                                types[blockKey],
                                capsuleKey=='-'?null:capsules[capsuleKey],
                                new Rectangle(walkingOffset.X, 
                                walkingOffset.Y, 
                                blockSize.X, blockSize.Y))
                                );
                    }
                    walkingOffset.X += blockSize.X;
                }
                walkingOffset.Y += blockSize.Y;
                walkingOffset.X = offset.X;
            }
          }

        public bool HasActiveBlocks
        {
            get
            {
                foreach (Block b in _blocks)
                    if (b.IsActive)
                        return true;
                return false;
            }
        }

        public IEnumerable<Block> Blocks
        {
            get { return _blocks; }
        }
    }
}
